package com.qianxing.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qianxing.exceptionhandler.ResponseException;
import com.qianxing.mapper.TbMsgReviewMapper;
import com.qianxing.po.AddMsgDTO;
import com.qianxing.po.TbMsgDataPO;
import com.qianxing.po.TbMsgDataPO_FIX;
import com.qianxing.po.TbMsgReviewPO;
import com.qianxing.service.TbMsgDataService;
import com.qianxing.mapper.TbMsgDataMapper;
import com.qianxing.utils.MultipartFileToFileUtils;
import com.qianxing.utils.RedisContanct;
import com.qianxing.utils.TencentCOSFileUpload;
import com.qianxing.vo.TbMsgDataVO;
import com.qianxing.vo.TbMsgReviewVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;

import static com.qianxing.utils.RedisContanct.MSG_DATA_ALL;

/**
 * @author JasonLong
 * @description 针对表【tb_msg_data(推文数据表)】的数据库操作Service实现
 * @createDate 2023-07-09 22:13:05
 */
@Service
public class TbMsgDataServiceImpl extends ServiceImpl<TbMsgDataMapper, TbMsgDataPO> implements TbMsgDataService {

    private Logger LOGGER = LoggerFactory.getLogger(TbMsgDataServiceImpl.class);

    @Autowired
    private TbMsgDataMapper msgDataMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static final ObjectMapper mapper = new ObjectMapper();

    /**
     * 查询所有文章数据
     *
     * @return
     * @throws JsonProcessingException
     */
    @Override
    public ResponseException queryAllData() throws JsonProcessingException {
        //先从Redis查询
        String rd = stringRedisTemplate.opsForValue().get(MSG_DATA_ALL);
        //如果Redis存在，则直接返回Redis数据
        if (!StrUtil.isBlank(rd)) {
            //反序列化
            List<TbMsgDataVO> msgVOS = mapper.readValue(rd, List.class);
            LOGGER.info("====Redis data>>>====" + msgVOS);
            return ResponseException.success(msgVOS);
        }
        //获取到所有文章数据
        List<TbMsgDataPO> tbMsgDataPOS = msgDataMapper.selectList(null);
        LOGGER.info("===tbMsgDataPOS:===" + tbMsgDataPOS);
        //使用流读取PO转化为VO
        List<TbMsgDataVO> tbMsgDataVOS = tbMsgDataPOS.stream().map(m -> {
            //建立view
            TbMsgDataVO tbMsgDataVO = new TbMsgDataVO();
            //文章标题
            tbMsgDataVO.setMsgTitle(m.getMsgTitle());
            //文章内容
            tbMsgDataVO.setMsgData(m.getMsgData());
            //文章配图
            if (!(m.getMsgImg() == null || m.getMsgImg().equals(""))) {
                tbMsgDataVO.setMsgImg(JSON.parseArray(m.getMsgImg()));
            } else {
                //为空则返回null
                tbMsgDataVO.setMsgImg(null);
            }
            //文章ID
            tbMsgDataVO.setMsgId(m.getMsgId());
            //文章收藏状态
            tbMsgDataVO.setMsgBeCollection(m.getMsgBeCollection());
            //文章评论
            QueryWrapper queryReviewCondition = new QueryWrapper<>();
            queryReviewCondition.eq("msg_id", m.getMsgId());
//            //查询该文章下的评论<FIX:文章评论获取已经修改为独立的方法>
//            List<TbMsgReviewVO> tbMsgReviewVOS = msgReviewMapper.selectList(queryReviewCondition);
//            tbMsgDataVO.setMsgReviewData(tbMsgReviewVOS);
            return tbMsgDataVO;
        }).collect(Collectors.toList());
        //序列化
        String jsonString = JSON.toJSONString(tbMsgDataVOS);
        LOGGER.info("===Get json data:===" + jsonString);
        LOGGER.info("===Get TbMsgDataVOS:===" + tbMsgDataVOS);
        //存入redis
        stringRedisTemplate.opsForValue().set(MSG_DATA_ALL, jsonString, 120L, TimeUnit.MINUTES);
        return ResponseException.success(tbMsgDataVOS);
    }

    @Value("${upload.messageServerUrl}")
    private String messageServerUrl;

    /**
     * 新增文章
     *
     * @param addMsgDTO
     * @return
     */
    @Transactional
    @Override
    public ResponseException addMsg(AddMsgDTO addMsgDTO) {
        if (addMsgDTO.getMsgTitle() == null || addMsgDTO.getMsgTitle().equals("")) {
            return ResponseException.error("500", "标题不能为空！");
        }
        if (addMsgDTO.getMsgData() == null || addMsgDTO.getMsgData().equals("")) {
            return ResponseException.error("500", "文章内容不能为空！");
        }
        //TODO 将文件取出
        List<MultipartFile> fileList = addMsgDTO.getMultipartFile();
        //定义数组存储文件名
        List<String> fileNames = new ArrayList<>();
        //TODO 将文件遍历存储到服务器中
        fileList.forEach(item -> {
            //原始文件名
            String originalFilename = item.getOriginalFilename();
            //文件后缀名(包括.)
            String substring = originalFilename.substring(originalFilename.indexOf("."), originalFilename.length());
            //新文件名
            String newFileName = "filename" + RandomUtil.randomString(12) + substring;

            File file = MultipartFileToFileUtils.multipartFileToFile(item);

            //存入腾讯云存储桶
            String fileUrl = TencentCOSFileUpload.uploadfile(file, newFileName, messageServerUrl);
            fileNames.add(fileUrl);
        });
        TbMsgDataPO tbMsgDataPO = new TbMsgDataPO();
        tbMsgDataPO.setMsgTitle(addMsgDTO.getMsgTitle());
        tbMsgDataPO.setMsgData(addMsgDTO.getMsgData());
        tbMsgDataPO.setMsgId(addMsgDTO.getMsgId());
//        tbMsgDataPO.setMsgImgName(JSONObject.toJSONString(fileNames));

        //获取锁
        Boolean lock = tryLock(addMsgDTO.getMsgId());
        try {
            if (!lock) {
                //线程睡眠50ms
                Thread.sleep(50);
                //递归调用
                addMsg(addMsgDTO);
            } else {
                //更新数据库
                QueryWrapper queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("msg_id", addMsgDTO.getMsgId());
                List list = msgDataMapper.selectList(queryWrapper);
                //当数据库中已存在相同字段值时，直接返回成功，但不做数据库更新（针对多文件上传多次发送请求）
                if (list.size() > 0) {
                    return ResponseException.success();
                }
                //数据库不存在，直接存入
                boolean save = save(tbMsgDataPO);
                if (!save) {
                    return ResponseException.error("500", "添加失败！");
                }
                //删除缓存
                stringRedisTemplate.delete(MSG_DATA_ALL);
                //释放锁
                unLock(addMsgDTO.getMsgId());
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return ResponseException.success();
    }

    /**
     * 删除文章
     *
     * @param msgId
     * @return
     */
    @Override
    public ResponseException delMsg(String msgId) {
        int deleted = msgDataMapper.delMsgByMsgId(msgId);
        if (!(deleted > 0)) {
            return ResponseException.error("500", "删除失败");
        }
        return ResponseException.success("200", "删除成功");
    }

    /**
     * 设置用户当前访问的文章的文章id
     *
     * @param msgId
     * @return
     */
    @Override
    public ResponseException setCurrentMsgKey(String msgId, String userId) {
        //键为：固定前缀＋用户唯一id
        String key = RedisContanct.CURRENT_MSG_ID + userId;
        stringRedisTemplate.opsForValue().set(key, msgId, 20L, TimeUnit.MINUTES);//过期时间20分钟
        return ResponseException.success(key);//将键返回
    }

    /**
     * 获取当前阅读文章数据
     *
     * @param userId
     * @return
     */
    @Override
    public ResponseException getCurrentMsg(String userId) {
        String key = RedisContanct.CURRENT_MSG_ID + userId;

        //从Redis获取文章id
        String msgId = stringRedisTemplate.opsForValue().get(key);
        System.err.println("MSGID:" + msgId);
        if ("".equals(msgId)) {
            return null;//传入的文章id为空时则直接返回空
        }
        //获取指定文章 FIXME 1 当前问题：Invalid Binding问题； FIXME 2 偶尔会出现数据库连接超时异常，需要排查解决
        TbMsgDataPO msgData = msgDataMapper.getByMsgId(msgId);
        TbMsgDataPO_FIX msgDataPOFix = new TbMsgDataPO_FIX();//此类{TbMsgDataPO_FIX}目的是将TbMsgPO类的部分属性类型转为指定的数据类型
        msgDataPOFix.setId(msgData.getId());
        msgDataPOFix.setMsgId(msgId);
        msgDataPOFix.setMsgData(msgData.getMsgData());
        msgDataPOFix.setMsgTitle(msgData.getMsgTitle());
        msgDataPOFix.setMsgBeCollection(msgData.getMsgBeCollection());
        //将String类型转为List类型
        //文章配图
        if (!(msgData.getMsgImg() == null || msgData.getMsgImg().equals(""))) {
            msgDataPOFix.setMsgImg(JSON.parseArray(msgData.getMsgImg()));
        } else {
            //为空则返回null
            msgDataPOFix.setMsgImg(null);
        }
        //文章配图源文件名
        if (!(msgData.getMsgImgName() == null || msgData.getMsgImgName().equals(""))) {
            msgDataPOFix.setMsgImgName(JSON.parseArray(msgData.getMsgImgName()));
        } else {
            //为空则返回null
            msgDataPOFix.setMsgImgName(null);
        }

        System.err.println(JSON.toJSONString(msgDataPOFix));
        return ResponseException.success(msgDataPOFix);
    }

    //获取锁
    public Boolean tryLock(String msgIds) {
        Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent(msgIds, msgIds);
        return aBoolean;
    }

    //释放锁
    public Boolean unLock(String msgIds) {
        Boolean delete = stringRedisTemplate.delete(msgIds);
        return delete;
    }
}




